﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mogre;

namespace WastedMechanics
{
    public class GuiManager
    {
        public List<GuiFrame> Layers;
        public GuiControl Pointer;
        
        public GuiFrame DragLayer;
        public object DropDest;
        public Vector2 DragOffset;
        public float Proportions;

        public GuiControl FocusedControl;

        public event Predicate<GuiControl> OnScreenDropAccept;
        public event Action<GuiControl> OnScreenDrop;

        public GuiManager()
        {
            Layers = new List<GuiFrame>();

            GuiFrame pointerLayer = new GuiFrame("MousePointer", 255);
            Pointer = new GuiControl("MousePointer");
            Pointer.IsVisible = false;
            pointerLayer.SetControl(Pointer);

            DragLayer = new GuiFrame("DragLayer", 254);
            Proportions =  (4.0f / 3.0f) /Engine.Camera.AspectRatio ;
        }

        public void DropDownControl(object dest)
        {
            if (DragControl != null)
            {
                GuiControl oldControl = DragControl;
                // We must unplug control before plugging it to it's parent
                DragControl = null;
                if (dest != null)
                {
                    if (dest is string)
                    {
                        GuiFrame newFrame = NewLayer(dest as string);
                        newFrame.SetControl(oldControl);
                    }
                    else
                        (dest as GuiControl).DropReceive(oldControl);
                }
                else
                {
                    if (OnScreenDrop != null)
                        OnScreenDrop(oldControl);

                    oldControl.Destroy();
                }
            }
        }

        public void LeaveDragControl()
        {
            DropDownControl(DropDest);
        }


        public void DragUpControl(GuiControl control)
        {
            if (control != null)
            {
                // If control has a parent then it must be detached
                // from it. Else it is a root of a frame.
                if (control.Parent != null)
                {
                    DropDest = control.Parent;
                    if (!control.DragBegin())
                        control.Detach();
                }
                else
                {
                    GuiFrame oldLayer = Layers.Find(layer => layer.Control == control);
                    oldLayer.SetControl(null);
                    DropDest = oldLayer.Name;
                    
                }

                DragLayer.SetControl(control);
                UpdateDragControlPosition();
            }
            else
            {
                DragLayer.SetControl(null);
                DropDest = null;
            }
        }


        public GuiControl DragControl
        {
            get
            {
                return DragLayer.Control;
            }
            set
            {
                DragUpControl(value);                                                                           
            }
        }

        private void UpdateDragControlPosition()
        {
            DragControl.Left = Engine.MouseInput.Position.x - Engine.GuiManager.DragOffset.x;
            DragControl.Top = Engine.MouseInput.Position.y - Engine.GuiManager.DragOffset.y;
        }       
        
        public GuiFrame NewLayer(string name)
        {
            ushort zorder = 1;
            if (Layers.Count > 0)
                zorder = (ushort)System.Math.Min(253, Layers.Max(l => l.Overlay.ZOrder) + 1);

            GuiFrame layer = new GuiFrame(name, zorder);
            Layers.Insert(0,layer);
            return layer;
        }

        public void MoveLayerToTop(GuiFrame layer)
        {
            layer.Overlay.ZOrder = Layers.First().ZOrder;

            int topi = Layers.IndexOf(layer);
            for (int i = 0; i < topi; i++)
                Layers[i].Overlay.ZOrder--;

            Layers.RemoveAt(topi);
            Layers.Insert(0, layer);

            if (layer.Control != null)
                FocusedControl = layer.Control;
        }

        public void GiveKeyboardInput()
        {
            if (FocusedControl != null && FocusedControl.IsVisible)
            {                
                foreach (MOIS.KeyCode key in Enum.GetValues(typeof(MOIS.KeyCode)))
                {
                    if (Engine.IsKeyTyped(key))                                            
                        FocusedControl.KeyTyped(key);                    
                }
            }
        }

        public void Update()
        {
            Pointer.Left = Engine.MouseInput.Position.x;
            Pointer.Top = Engine.MouseInput.Position.y;

            if (DragControl != null)
            {
                UpdateDragControlPosition();

                if (!Engine.MouseInput.ButtonDown(MOIS.MouseButtonID.MB_Left))
                {
                    object dest = DropDest;
                    bool onScreen = true;
                    GuiFrame top = Layers.Find(l => l.Control.IsVisible && l.ContainsPoint(Engine.MouseInput.Position));
                    if (top != null)
                    {                        
                        GuiControl newDest = top.Control.DropAccept(DragControl);                        
                        if (!top.IsZOrderFixed) // Fixed layers are fixed. And Transparent.
                            onScreen = false;
                        if (newDest != null)
                            dest = newDest;                                                    
                    }
                    if (onScreen)
                    {
                        if (OnScreenDropAccept != null)
                            if (OnScreenDropAccept(DragControl))
                                dest = null;
                    }
                    DropDownControl(dest);
                }
            }
            else
            if (Layers.Count > 0)
            {
                foreach (GuiFrame layer in Layers)
                    layer.Update();

                GuiFrame top;
                // Mouse press event can trigger layer reordering zorder. Clicked
                // layer will became top layer
                if (Engine.MouseInput.ButtonPressed(MOIS.MouseButtonID.MB_Left) || Engine.MouseInput.ButtonPressed(MOIS.MouseButtonID.MB_Right))
                {
                    Layers.Sort((a, b) => b.Overlay.ZOrder.CompareTo(a.Overlay.ZOrder));
                    top = Layers.Find(l => 
                        l.Control != null 
                        && l.ContainsPoint(Engine.MouseInput.Position) 
                        && l.Control.IsVisible 
                        && l.IsZOrderFixed == false);
                    if (top == null)
                    {
                        top = Layers.First();
                        FocusedControl = null;
                    }

                    if (top != Layers.First())                    
                        MoveLayerToTop(top);                    
                }
                else
                    top = Layers.First();

                top.GiveInput();
                GiveKeyboardInput();

                // Removing all layers that have no control on it or its control is destroyed
                Layers.RemoveAll(layer => 
                    {
                        if (layer.Control == null || layer.Control.IsDestroyed)
                        {
                            layer.Destroy();
                            return true;
                        }
                        return false;
                    } );
            }
        }
    }
}
